# Copyright 2011-2011 by iNeed,Inc. All Rights Reserved.
#-*- encoding:utf-8 -*-

import sys, os, time, string,re

import socket
import database
import sock_asyncore
import msgHandler,gridManager
from common import *
import logging,traceback
__author__  = "Number Huang<huang.bafeng@iNeed.com>"
__status__  = "developing"
__version__ = "0.1108.13.1"


class SessionPeer(object):
    def __init__(self,owner):
        self.owner= owner
        self.log  = owner.log
        self.listenPort = 0 #phone,ip,port,listenPort
        self.phone = None
        
    def getSession(self):
        return self.owner
    
    def setIP(self,ip,port):
        self.ip = ip
        self.port = port 
    
    def getIP(self):
        return self.ip,self.port
    
    def setPhone(self,phone):
        self.phone = phone  
    
    def getPhone(self):
        return  self.phone
    
    def setListenPort(self,port):
        self.listenPort = port
    
    def getListenPort(self):
        return self.listenPort
    
class DriverCapability(object):
    """
    the capability represents the time-space-number ability
    the DC belongs to a departGrid[departTimeIdx] and 
    if the destGrid exists,it means it is a pooling ability
   
    """
    def __init__(self,id,timeIdx,curGrid,seats=None,destGrid=None):#
         
        self.id = id
        self.lock= threading.Condition()
        if not seats:
            seats = configall.MAXPERSON
        self.setAll(timeIdx, curGrid, seats, destGrid)
        self.rejectCount = 0 #CA reject count, which can be used for a better choose strategy
        self.status = EDC_ACTIVE
        self.curGrid.addOffer(self)
        
    
    def setStatus(self,status):
        with AutoLock(self,self.lock):
            self.status = status
    
    def getStatus(self):
        with AutoLock(self,self.lock):
            return self.status
        
    def setDestGrid(self,destGrid):
        self.destGrid = destGrid# None means anywhere,else poolingDest
    
    def setCurGrid(self,curGrid):
        self.curGrid = curGrid
    
    def setRemaindSeats(self,seats):
        self.seats = seats
    
    def setTimeIdx(self,timeIdx):
        self.timeIdx = timeIdx  
    
    def setPoolingID(self,poolingID): 
        self.poolingID = poolingID
    
    def setRejectCount(self,num=1):
        self.rejectCount += num 
    
    def isOrdered(self):
        with AutoLock(self,self.lock):
            return self.seats!=configall.MAXPERSON
    
    def isPooling(self):
        with AutoLock(self,self.lock):
            return self.destGrid
    
    def setAll(timeIdx,curGrid,seats,destGrid=None,poolingID=None):
        with AutoLock(self,self.lock):
            self.timeIdx = timeIdx
            self.curGrid = curGrid
            self.seats   = seats
            self.destGrid=destGrid
            self.poolingID=poolingID
    """
    def canPooling(self):
        return self.destGrid
    
    def isNeedUpdate(self,timeIdx,curGrid,seats,destGrid): 
        return self.timeIdx != timeIdx  or  self.curGrid != curGrid or self.seats != seats or self.destGrid != destGrid
    """
class SessionDriver(SessionPeer):
    """
        when after connect or after accept, the (ip,port) is got.
        And for each session, the phone can be got from locationParams.
        About the listen port, it will be known in login info, and stored in server.
        Peers do not know the listen port each other, rather, server will tell them this.
        Currently, only driver session needs the object
        
        The main objective is to maintain the capability
        capabilityDict[pubID]=
        when to change the ability?
        1,submit order #NewAbility
        if it is new order
            a remote ability is created at (destTime,destGrid)
        else if it is a pooling order#TODO
            it may change to (newdestTime,newdestGrid)
        
        2,execute order #curAbility
        if pooling:
            [seats] = leftSeats
            [dest] = dest
        else:
            [seats] = 0
        
        3,finish order #curAbility
        if IDLE:
            [seats]=4
            [dest]=None
        else:#go on executing another order
            [seats]=leftSeats
            [dest]=newPoolingDest
        
    """
    def __init__(self,owner):
        super(SessionDriver,self).__init__(owner)
        #self.grid = None
        self.status   = ES_IDLE
        self.nextTime = 0
        self.onCallID = INVALID_PUBID #
        
        self.gridManager = gridManager.gridManagerFactory()
        #self.activePoolingOrder = None
        self.db = database.CreateDB(self)
        self.lock= threading.Condition()
        self.curAbility = None#DriverCapability(self.phone,configall.getTimeIdx(),None) 
        #self.poolingAbilityDict={}    
        #self.remoteAbility={}
        self.abilityDict={} #[idx] for each timeIdx, it has one ability at most
    
    def _addDC(self,dc):
        self.abilityDict[idx] = dc
        
    def _deleteDC(self,dc):  
        del self.abilityDict[dc.timeIdx]
        dc.setStatus(EDC_CLOSED)
    
    def _updateDC(self,idx,curGrid,seats,destGrid,poolingID):
        """
        1,order done
            if 
        """
        dc = self.abilityDict.get(idx)
        dc.setAll(curGrid,seats,destGrid,poolingID)
                        
    def updateDriverStatus(self,params,pos):
      with AutoLock(self,self.lock):
        self.status = params["status"]
        curGrid = self.gridManager.getTheGrid(pos['x'],pos['y'])
        timeIdx = configall.getTimeIdx(pos['Time'])
        seats   = params.get(EDS_REMAINEDSEATS,0)#configall.MAXPERSON)
        destGrid = None
               
        if params.has_key("pubID"):
            pubID = params["pubID"]
            if self.status==ESE_ORDER_DONE:
                """
                1, a remote DC at (destTime,destGrid) seat=4,dest=anywhere
                2, if poolingEnabled, a remote DC at(departTime,departGrid),seat=4-curNum,dest=destGrid
                    the following order may change the 1 because the new order will be the mainOrder
                    and the 2,which needs to update the 
                TODO: the timeIdx should be the departTime which is used for a key when executing order
                """
                
                curGrid2 = self.gridManager.getTheGrid(params['x2'],params['y2'])
                timeIdx2 = configall.getTimeIdx(params[EDS_DESTTIME]+configall.TIMEZONE)#TODO,currently next Timezone for possible delay
                
                if not self.abilityDict.has_key(timeIdx2):
                    dc = DriverCapability(self.phone,timeIdx2,curGrid2)
                    self._add(dc)

                poolingID = params.get(EDS_POOLINGID)    
                if poolingID:  
                                  
                    if not self.abilityDict.has_key(timeIdx):
                        dc = DriverCapability(self.phone,timeIdx,curGrid,seats,curGrid2)
                        dc.setPoolingID(poolingID)
                        self._add(dc)   
                    else:
                        self._update(timeIdx,curGrid,seats,curGrid2,poolingID)
                return 
            
            elif self.status==ESE_GOFOR_PICKUP:
                poolingID = params.get(EDS_POOLINGID)    
                if poolingID:
                    dc = self.poolingAbilityDict[poolingID]
                    if dc!=self.curAbility:
                        self._deleteTheDCFromGrid(self.curAbility)
                        self.curAbility = dc
                    
                self.curAbility.setRemaindSeats(seats)
                #destGrid = self.gridManager.getTheGrid(params['x2'],params['y2'])
                    
            elif  self.status==ESE_TRANSATION_DONE: 
                if params.get(EDS_POOLINGID): 
                    self.curAbility.setRemaindSeats(seats)    
                else:
                    self._deleteTheDCFromGrid(self.curAbility)
                    self.curAbility = None
                
        #else:#normal uds,only update time/space
        self.curAbility = self.abilityDict.get(timeIdx)
        if not self.curAbility:
            self.curAbility = DriverCapability(self.phone,timeIdx,curGrid)
            
        if self.curAbility and self.curAbility.seats:#IDLE or is executing pooling order
            if timeIdx!=self.curAbility.timeIdx or curGrid!=self.curAbility.curGrid:
                self._deleteTheDCFromGrid(self.curAbility)
                self.curAbility.curGrid = curGrid
                self.curAbility.timeIdx = timeIdx
                curGrid.addOffer(self.curAbility)  
                    #self.log.debug("the gridpos(%d,%d)->(%d,%d),pooling=%d"%(pos['x'],pos['y'],self.grid.getRowCol()[0],self.grid.getRowCol()[1],isPooling))
        
    def setOnCall(self,value):
        self.onCallID = value
    
    def isOnCall(self):
        return self.onCallID!=INVALID_PUBID
    
   
  
                
if __name__=="__main__":

        msg="'%s'"%"what"
        print msg

